package base.thread_JUC.demo06;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 传统的方式实现生产者-消费者模式（采用 jdk1.5 方式，而不是更为古老的 synchronized）
 * synchronized 被 --> lock 取代
 * wait 被 --> await 取代
 * notify 被--> signal 取代
 *
 *
 * 使用分别5个线程生产者，和消费者线程，轮询输出1,0
 *
 * 1、线程     操作(方法)      资源类
 * 2、判断     阻塞           去通知
 * 3、防止虚假唤醒机制(while 而非if)
 *
 * synchronized 和 lock 的区别，用新的lock的好处是什么？
 * 1、原始构成
 * synchronized是关键字，属于JVM层面：
 *      monitorenter（底层是通过对象监视器monitor对象来完成，wait/notify等方法也依赖于monitor对象，只有在同步块或方法中才能调用wait/notify等方法）
 *      monitorexit
 * Lock是具体类（JUC包下），是API层面的锁。
 * 2、使用方法
 * synchronized不需要手动去释放锁，当synchronized代码执行完毕，系统自动让线程释放对锁的占用（调用两次monitorexit，一次正常退出，一次如果出现异常，也能保证退出）
 * ReentrantLock需要用户手动释放锁，否则就有可能导致死锁现象，需要lock()和unlock()方法配合try/finally语句块来完成。
 * 3、等待是否可中断
 *  synchronized不可中断，除非排除异常或者正常运行完成
 *  ReentrantLock可中断
 * 4、加锁是否公平
 *  synchronized 非公平锁
 *  ReentrantLock可设置是否公平，默认非公平，效率高
 * 5、锁绑定多个条件Condition
 *  synchronized 没有
 *  ReentrantLock用来实现分组唤醒需要唤醒的线程们，可以精确唤醒，而不是像synchronized要么随机唤醒，要么唤醒一个线程，要么唤醒全部线程。
 *
 */
public class ProdConsumer_TraditionDemo {
    public static void main(String[] args) {
        ShareData sd = new ShareData();

        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                sd.decrement();
            },"AA").start();
        }

        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                sd.increment();
            },"BB").start();
        }
    }
}

class ShareData {
    private int number = 0;
    Lock lock = new ReentrantLock(false);
    Condition condition = lock.newCondition();

    // +1
    public void increment() {
        try {
            lock.lock();
            while (number != 0) {
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"\t "+number);
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    // -1
    public void decrement()  {
        try {
            lock.lock();
            while (number == 0) {
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"\t "+number);
            condition.signalAll();
        }catch (Exception e){

        }finally {
            lock.unlock();
        }
    }
}
